#include "xrtc/rtc/modules/rtp_rtcp/rtcp_packet/transport_feedback.h"
#include <rtc_base/logging.h>
#include <modules/rtp_rtcp/source/byte_io.h>
#include <absl/algorithm/container.h>
namespace xrtc {

	namespace rtcp {
		namespace {
			const size_t  kRtcpTransportFeedbackHeaderSize = 4 + 8 + 8;
			const size_t kMinPayloadSizeBytes = 8 + 8 + 2;
			const size_t kChunkSizeBytes = 2;
		
		}//namespace
		bool TransportFeedback::Parse(const rtcp::CommonHeader& packet)
		{
			//check length
			if (packet.payload_size() < kMinPayloadSizeBytes)
			{
				RTC_LOG(LS_WARNING) << "Buffer is too small for a transport feedback packet,payload_size: " << packet.payload_size()
					<< ", min payload_size: " << kMinPayloadSizeBytes;
				return false;
			}
			const uint8_t* payload = packet.payload();

			ParseCommonFeedback(payload);

			base_seq_no_ = webrtc::ByteReader<uint16_t>::ReadBigEndian(&payload[8]);
			uint16_t status_count = webrtc::ByteReader<uint16_t>::ReadBigEndian(&payload[10]);
			base_time_ticks_ = webrtc::ByteReader<int32_t, 3>::ReadBigEndian(&payload[12]);
			feedback_seq_ = payload[15];
			if (0 == status_count)
			{
				RTC_LOG(LS_WARNING) << "Empty transport feedback message not allowed size is 0 !";
				return false;
			}
			Clear();
			size_t index = 16;
			size_t end_index = packet.payload_size();
			std::vector<uint8_t> delta_sizes;
			delta_sizes.reserve(status_count);

			while (delta_sizes.size() > status_count)
			{
				if (index + kChunkSizeBytes > end_index) {
					RTC_LOG(LS_WARNING) << "Buffer overlow when parsing packet";
					Clear();
					return false;
				}
				//read a chunk
				uint16_t chunk = webrtc::ByteReader<uint16_t>::ReadBigEndian(&payload[index]);
				index += kChunkSizeBytes;
				last_chunk_.Decode(chunk, status_count - delta_sizes.size());
				last_chunk_.AppendTo(&delta_sizes);
			}
			uint16_t seq_no = base_seq_no_;
			size_t recv_delta_size = absl::c_accumulate(delta_sizes, 0);
			if (end_index >= index + recv_delta_size) {
				
				for (size_t delta_size : delta_sizes) {
					if (index + delta_size > end_index)
					{
						RTC_LOG(LS_WARNING) << "Buffer overlow when parsing packet";
						Clear();
						return false;
					}
					switch (delta_size)
					{
					case 0:
						if (include_lost_)
						{
							all_packets_.emplace_back(seq_no);
						}
						break;
					case 1:
					{
						int16_t delta = payload[index];
						received_packets_.emplace_back(seq_no, delta);
						if (include_lost_)
						{
							all_packets_.emplace_back(seq_no, delta);
						}
						index += delta_size;
						break;
					}
					case 2:
					{
						int16_t delta = webrtc::ByteReader<int16_t>::ReadBigEndian(&payload[index]);
						received_packets_.emplace_back(seq_no, delta);
						if (include_lost_)
						{
							all_packets_.emplace_back(seq_no, delta);
						}
						index += delta_size;
						break;
					}
					case 3:
						Clear();
						RTC_LOG(LS_WARNING) << "invalid delta size for deq_no: "<<seq_no;
						return false;
					default:break;
					}
					++seq_no;
				}
			}
			else
			{
				include_timestamps_ = false;
				for (size_t delta_size : delta_sizes)
				{
					if (delta_size > 0)
					{
						received_packets_.emplace_back(seq_no, 0);
					}
					if (include_lost_)
					{
						if (delta_size > 0) {
							all_packets_.emplace_back(seq_no,0);
						}
						else
						{
							all_packets_.emplace_back(seq_no);
						}
					}
					++seq_no;

				}
			}

			size_bytes_ = RtcpPacket::kHeaderSize + index;
			return true;
		}
		void TransportFeedback::Clear()
		{
		/*	base_seq_no_ = 0;
			base_time_ticks_ = 0;
			feedback_seq_ = 0;*/
			last_chunk_.Clear();
			all_packets_.clear();
			received_packets_.clear();
			size_bytes_ = kRtcpTransportFeedbackHeaderSize;
		
		}

		size_t TransportFeedback::BlockLength() const
		{
			return 0;
		}
		bool TransportFeedback::Create(uint8_t* packet, size_t* index, size_t max_length, PacketReadyCallback callback) const
		{
			return false;
		}
		void TransportFeedback::LastChunk::Decode(uint16_t chunk, size_t max_size)
		{
			if (0 == chunk & 0x8000)
			{
				//run length encode block
				DecodeRunLength(chunk, max_size);
			}
			else if(0==(chunk& 0x4000)) //1bit status vec
			{
				DecodeOneBit(chunk, max_size);
			}
			else //2bit status  block
			{
				DecodeTwoBit(chunk, max_size);
			}
		}
		void TransportFeedback::LastChunk::Clear()
		{
			size_ = 0;
			all_same_ = true;
			has_large_delta_ = false;

		}

		void TransportFeedback::LastChunk::AppendTo(std::vector<uint8_t>* deltas)
		{
			if (all_same_) {
				deltas->insert(deltas->end(), size_, delta_sizes_[0]);
			}
			else
			{
				deltas->insert(deltas->end(), delta_sizes_, delta_sizes_ + size_);
			}

		}
		void TransportFeedback::LastChunk::DecodeRunLength(uint16_t chunk, size_t max_size)
		{
			size_ = std::min<size_t>(chunk & 0x1FF, max_size);
			all_same_ = true;
			//rtp status value
			uint8_t delta_size = (chunk >> 13)& 0x3;
			has_large_delta_ = (delta_size > kLarge);
			delta_sizes_[0] = delta_size;
		}
		void TransportFeedback::LastChunk::DecodeOneBit(uint16_t chunk, size_t max_size)
		{
			size_ = std::min(kOneBitCapacity, max_size);
			all_same_ = false;
			has_large_delta_ = false;
			for (size_t i = 0; i < size_; ++i) {
				delta_sizes_[i] = (chunk >> (kOneBitCapacity - 1 - i)) & 0x01;
			}
		}
		void TransportFeedback::LastChunk::DecodeTwoBit(uint16_t chunk, size_t max_size)
		{

			size_ = std::min(kTwoBitCapacity, max_size);
			all_same_ = false;
			has_large_delta_ = true;
			for (size_t i = 0; i < size_; ++i) {
				delta_sizes_[i] = (chunk >> (2*(kTwoBitCapacity - 1 - i))) & 0x03;
			}
		}
	}//namespace rtcp
	 

}//namespace xrtc